]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Make startup animation use async functions throughout
authorWill Thompson <wjt@endlessos.org>
Fri, 19 Jul 2024 13:11:15 +0000 (14:11 +0100)
committerFlorian Müllner <fmuellner@gnome.org>
Sat, 3 Aug 2024 18:05:04 +0000 (20:05 +0200)
During session startup, we have the following chain:

    LayoutManager._prepareStartupAnimation()
    `- LayoutManager._startupAnimation()
       `- LayoutManager._startupAnimationComplete()       (a)
       | or
       `- LayoutManager._startupAnimationGreeter()        (b)
       | or
       `- LayoutManager._startupAnimationSession()        (c)
          `- Overview.runStartupAnimation()               (d)
             `- OverviewActor.runStartupAnimation()       (e)
                `- ControlsManager.runStartupAnimation()  (f)

Branch (b) calls LayoutManager._startupAnimationComplete() once the
animation is complete. Branch (c) does the same, except that
ControlsManager.runStartupAnimation() is an async function that awaits
LayoutManager.ensureAllocation().

LayoutManager._prepareStartupAnimation() is an `async` function, and its
caller catches & logs any error it raises.

Previously, ControlsManager.runStartupAnimation() – (f) in the above
diagram – was declared `async`, because it uses `await` internally, but
also accepted a callback, which was called on successful completion of
the function and the animation it triggers.

If an exception is raised during execution of the function, its callback
would never be called, and because the promise it implicitly returns is
discarded, the exception would never be logged either. And, stepping a
few levels up the call stack, the callback that
LayoutManager._startupAnimationSession() (c) provided would never be
called, and so LayoutManager._startupAnimationComplete() would never be
called, meaning the cover pane that prevents input would never be
removed and the session would be unusable.

Remove the callback parameter from ControlsManager.runStartupAnimation()
(f), and instead make it resolve in the case where the callback would
previously have been called.

Remove the callback parameter from OverviewActor.runStartupAnimation()
(e) – it is just a wrapper around ControlsManager.runStartupAnimation().

Adjust Overview.runStartupAnimation() (d) accordingly: rather than
passing a callback to OverviewActor.runStartupAnimation(), await its
return and then proceed. There is a slight behaviour change here:
previously, in the branch where this._syncGrab() is false, the callback
would be called before calling this.hide(), whereas now this.hide() is
called before the async function returns.

Adjust LayoutManager._startupAnimationSession() and its siblings to be
async, rather than each calling _startupAnimationComplete() directly.
Finally, in _prepareStartupAnimation()  await _startupAnimation() either
succeeding or failing, and in either case call
_startupAnimationComplete(), removing the cover pane that prevents all
input.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3428>
(cherry picked from commit 3d061345456b483be19d5f49a9bd18d87491f31f)

js/ui/layout.js
js/ui/overview.js
js/ui/overviewControls.js

index 3b54960f84ef3f2ffa754a8f0704e6b268747b9c..5b09817ba3d0b5553adb78719deb3bffe76b40b6 100644 (file)
@@ -763,39 +763,47 @@ export const LayoutManager = GObject.registerClass({
         else
             this.emit('startup-prepared');
 
-        this._startupAnimation();
+        try {
+            await this._startupAnimation();
+        } finally {
+            this._startupAnimationComplete();
+        }
     }
 
-    _startupAnimation() {
+    async _startupAnimation() {
         if (Meta.is_restart())
-            this._startupAnimationComplete();
-        else if (Main.sessionMode.isGreeter)
-            this._startupAnimationGreeter();
+            return;
+
+        if (Main.sessionMode.isGreeter)
+            await this._startupAnimationGreeter();
         else
-            this._startupAnimationSession();
+            await this._startupAnimationSession();
     }
 
-    _startupAnimationGreeter() {
-        this.panelBox.ease({
-            translation_y: 0,
-            duration: STARTUP_ANIMATION_TIME,
-            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
-            onStopped: () => this._startupAnimationComplete(),
+    async _startupAnimationGreeter() {
+        await new Promise(resolve => {
+            this.panelBox.ease({
+                translation_y: 0,
+                duration: STARTUP_ANIMATION_TIME,
+                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                onStopped: () => resolve(),
+            });
         });
     }
 
-    _startupAnimationSession() {
-        const onStopped = () => this._startupAnimationComplete();
+    async _startupAnimationSession() {
         if (Main.sessionMode.hasOverview) {
-            Main.overview.runStartupAnimation(onStopped);
+            await Main.overview.runStartupAnimation();
         } else {
-            this.uiGroup.ease({
-                scale_x: 1,
-                scale_y: 1,
-                opacity: 255,
-                duration: STARTUP_ANIMATION_TIME,
-                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
-                onStopped,
+            await new Promise(resolve => {
+                this.uiGroup.ease({
+                    scale_x: 1,
+                    scale_y: 1,
+                    opacity: 255,
+                    duration: STARTUP_ANIMATION_TIME,
+                    mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                    onStopped: () => resolve(),
+                });
             });
         }
     }
index 7e431b68e32779d1ce4ac9c27702c337a26d59fe..2f40d2fd9f7f4291e11c903c7c0c1537cfbc46df 100644 (file)
@@ -90,8 +90,8 @@ class OverviewActor extends St.BoxLayout {
         this._controls.animateFromOverview(callback);
     }
 
-    runStartupAnimation(callback) {
-        this._controls.runStartupAnimation(callback);
+    async runStartupAnimation() {
+        await this._controls.runStartupAnimation();
     }
 
     get dash() {
@@ -666,7 +666,7 @@ export class Overview extends Signals.EventEmitter {
         this._overview.controls.appDisplay.selectApp(id);
     }
 
-    runStartupAnimation(callback) {
+    async runStartupAnimation() {
         Main.panel.style = 'transition-duration: 0ms;';
 
         this._shown = true;
@@ -679,23 +679,19 @@ export class Overview extends Signals.EventEmitter {
 
         this._changeShownState(OverviewShownState.SHOWING);
 
-        this._overview.runStartupAnimation(() => {
-            // Overview got hidden during startup animation
-            if (this._shownState !== OverviewShownState.SHOWING) {
-                callback();
-                return;
-            }
+        await this._overview.runStartupAnimation();
 
-            if (!this._syncGrab()) {
-                callback();
-                this.hide();
-                return;
-            }
+        // Overview got hidden during startup animation
+        if (this._shownState !== OverviewShownState.SHOWING)
+            return;
 
-            Main.panel.style = null;
-            this._changeShownState(OverviewShownState.SHOWN);
-            callback();
-        });
+        if (!this._syncGrab()) {
+            this.hide();
+            return;
+        }
+
+        Main.panel.style = null;
+        this._changeShownState(OverviewShownState.SHOWN);
     }
 
     getShowAppsButton() {
index 7f499aef7f6f179c07f9d2d5117847d3d71335e9..9a57ebb79239b8e545702dc2f7d92b6b96e46015 100644 (file)
@@ -804,7 +804,7 @@ class ControlsManager extends St.Widget {
         this._stateAdjustment.gestureInProgress = false;
     }
 
-    async runStartupAnimation(callback) {
+    async runStartupAnimation() {
         this._ignoreShowAppsButtonToggle = true;
 
         this.prepareToEnterOverview();
@@ -846,14 +846,16 @@ class ControlsManager extends St.Widget {
         });
 
         // The Dash rises from the bottom. This is the last animation to finish,
-        // so run the callback there.
+        // so resolve the promise there.
         this.dash.translation_y = this.dash.height + this.dash.margin_bottom;
-        this.dash.ease({
-            translation_y: 0,
-            delay: STARTUP_ANIMATION_TIME,
-            duration: STARTUP_ANIMATION_TIME,
-            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
-            onStopped: () => callback(),
+        return new Promise(resolve => {
+            this.dash.ease({
+                translation_y: 0,
+                delay: STARTUP_ANIMATION_TIME,
+                duration: STARTUP_ANIMATION_TIME,
+                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                onStopped: () => resolve(),
+            });
         });
     }